package munci.old.learn;

import munci.old.java.Type;
import munci.old.know.Env;
import munci.old.know.words.Constant;
import munci.old.know.words.MetaWord;
import munci.old.know.words.MethodWord;
import munci.old.know.words.Word;
import munci.old.use.Tokens;
import munci.read.Token;

public class Literals {

	public static final Word[] WORDS;

	static {
		WORDS = MetaWord.all(MethodWord.all(new Word.Book(Literals.class,
			new Constant("void", Type.VOID),
			new Constant("boolean", Type.BOOL),
			new Constant("char", Type.CHAR),
			new Constant("byte", Type.BYTE),
			new Constant("short", Type.SHORT),
			new Constant("int", Type.INT),
			new Constant("long", Type.LONG),
			new Constant("float", Type.FLOAT),
			new Constant("double", Type.DOUBLE),
			new Constant("true", true, boolean.class),
			new Constant("false", false, boolean.class)
		))).toArray();
	}
	
	// Literal parsers
	
	@MethodWord.Compile(Token.Literal.ID) 
	public static MethodWord.Continuation literal(final Token.Literal literal) {
		return new MethodWord.Continuation() {
			public void execute(Env env) {
				env.stack.push(new Tokens().add(literal), Tokens.class);
			}
		};
	}

	@MethodWord.Name(Token.Literal.ID) public static int parse(Token.Literal.Int tok) {
		String str = tok.image();
		if (str.startsWith("0") && str.length() > 1) {
			if ("Xx".indexOf(str.charAt(1)) > -1)
				return Integer.parseInt(str.substring(2), 16);
			else
				return Integer.parseInt(str.substring(1), 8);
		}
		return Integer.parseInt(str);
	}
	
	@MethodWord.Name(Token.Literal.ID) public static long parse(Token.Literal.Long tok) {
		String str = tok.image().substring(0, tok.image().length() - 1);
		if (str.startsWith("0") && str.length() > 1) {
			if ("Xx".indexOf(str.charAt(1)) > -1)
				return Long.parseLong(str.substring(2), 16);
			else
				return Long.parseLong(str.substring(1), 8);
		}
		return Long.parseLong(str);
	}
	
	//TODO: Hex floats
	@MethodWord.Name(Token.Literal.ID) public static float parse(Token.Literal.Float tok) {
		return Float.parseFloat(tok.image());
	}
	
	@MethodWord.Name(Token.Literal.ID) public static double parse(Token.Literal.Double tok) {
		return Double.parseDouble(tok.image());
	}
	
	@MethodWord.Name(Token.Literal.ID) public static String parse(Token.Literal.Str tok) {
		return decodeString(tok.image().substring(1, tok.image().length() - 1));
	}

	@MethodWord.Name(Token.Literal.ID) public static char parse(Token.Literal.Char tok) {
		return decodeChar(tok.image().substring(1, tok.image().length() - 1));
	}
	
	// Compilation
	
	private static String decodeString(String in) {
		StringBuilder str = new StringBuilder();
		int escape = -1;
		for (int i = 0; i < in.length(); i++) {
			char c = in.charAt(i);
			if (escape < 0 && c == '\\') {
				escape = i;
			} else if (escape < 0) {
				str.append(c);
			} else {
				String seq = in.substring(escape, i + 1);
				c = decodeChar(seq);
				if (c != 0) {
					str.append(c);
					escape = -1;
				}
			}
		}
		return str.toString();
	}
	
	public static String encodeString(String in) {
		StringBuilder str = new StringBuilder();
		for (char c : in.toCharArray()) {
			str.append(encodeChar(c));
		}
		return str.toString();
	}
	
	// TODO: 0ctal escape codes
	private static char decodeChar(String str) {
		char c = str.charAt(0);
		if (c == '\\') {
			switch (str.charAt(1)) {
			case 'b':   return '\b';
			case 't':   return '\t';
			case 'n':   return '\n';
			case 'f':   return '\f';
			case 'r':   return '\r';
			case '\"':  return '\"';
			case '\'':  return '\'';
			case '\\':  return '\\';
			default:    return 0;	
			}
		}
		return c;
	}
	
	public static String encodeChar(char c) {
		switch (c) {
		case '\b': case '\t': case '\n': case '\f': case '\r':
		case '\"': case '\'': case '\\':
			return "\\" + c;
		default:    return Character.toString(c);	
		}
	}
}